package com.kamistoat.meimeimall.meimeimallorder.web;

import com.alibaba.fastjson.JSON;
import com.kamistoat.meimeimall.common.To.memberTo.LoginSuccessTo;
import com.kamistoat.meimeimall.common.annotation.DubboAndHTTPExceptionCatch;
import com.kamistoat.meimeimall.common.annotation.LoginRequired;
import com.kamistoat.meimeimall.common.constant.AuthConstant;
import com.kamistoat.meimeimall.common.dubboInterface.ThirdPartyDubboInterface;
import com.kamistoat.meimeimall.common.exception.BizCodeEnum;
import com.kamistoat.meimeimall.common.exception.RRException;
import com.kamistoat.meimeimall.common.threadlocal.ThreadLocalContextHolder;
import com.kamistoat.meimeimall.common.utils.R;
import com.kamistoat.meimeimall.meimeimallorder.config.AlipayTemplate;
import com.kamistoat.meimeimall.meimeimallorder.feign.ThirdPartyFeignService;
import com.kamistoat.meimeimall.meimeimallorder.listener.OrderPayCallbackListener;
import com.kamistoat.meimeimall.meimeimallorder.service.OrderService;
import com.kamistoat.meimeimall.meimeimallorder.vo.OrderCheckVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.dubbo.config.annotation.Reference;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@Api(tags="商品详情页面")
@RestController
@RequestMapping("web")
public class ProductPageController {
    protected Logger logger = LoggerFactory.getLogger(ProductPageController.class);

    @Autowired
    OrderService orderService;
    @Autowired
    AlipayTemplate alipayTemplate;
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    ThirdPartyFeignService thirdPartyFeignService;
    @Autowired
    OrderPayCallbackListener orderPayCallbackListener;

    @Reference
    ThirdPartyDubboInterface thirdPartyDubboInterface;


    /**
     * 只有对于正在秒杀的商品，允许在商品详情页直接点击 '立刻抢购'，首先验证信号量锁的剩余可用量(仅检查，但不做扣减)。如果余量大于0，则放行秒杀请求至此接口，构建订单检查页。
     * 构建订单检查页时同样不会扣减信号量，只会校验信号量来判断是否有货。
     *
     * @param sessionProId 全局唯一的秒杀场次Id
     * @param randomCode   随机码
     * @param skuId
     * @param num
     * @return 订单确认页
     */
    @ApiOperation("秒杀商品详情页购买。前置限流器位于秒杀模块中")
    @PostMapping("/seckill_to_order_check")
    @DubboAndHTTPExceptionCatch
    @LoginRequired
    public R seckillToOrderCheck(@RequestParam Long sessionProId, @RequestParam String randomCode, @RequestParam Long skuId, @RequestParam Integer num) {
        // ThreadLocal中获取用户详情
        LoginSuccessTo loginSuccessTo = JSON.parseObject((String) ThreadLocalContextHolder.get(AuthConstant.LOGIN_USER_THREADLOCAL_NAME), LoginSuccessTo.class);
        /**
         * redisson限流器 order:orderCheck:rateLimit:userId.
         * 尽管该接口缓存为主，但还是在用户层面限制刷新频率
         */
        RRateLimiter rateLimiter = redissonClient.getRateLimiter("order:orderCheck:rateLimit:" + loginSuccessTo.getId());
        stringRedisTemplate.expire("order:orderCheck:rateLimit:" + loginSuccessTo.getId(), 5, TimeUnit.MINUTES);
        // 限流器初始化设置为每1秒提供5个令牌。如果限流器已存在，该初始化语句不会执行
        rateLimiter.trySetRate(RateType.OVERALL, 5, 1, RateIntervalUnit.SECONDS);
        // 获取限流器令牌，超时时间是2秒。因此只要当前只有一个账号实例，必然能够获取到
        boolean tryAcquire = rateLimiter.tryAcquire(1, 2, TimeUnit.SECONDS);

        R toTradeR = new R();

        // 没有被限流才放行之后的操作
        if (tryAcquire) {
            // 获取构建秒杀订单确认页所需要的数据
            OrderCheckVo orderCheckVo = orderService.checkSeckillOrder(sessionProId, randomCode, skuId, num);
            // OrderCheckVo中含三个PRC变量，都有可能出现异常。
            // 最核心的是选中商品列表 orderItemVoList 和 库存表 hasStockMap。
            // 只要这两个非空就构建订单检查页，否则直接返回购物车
            if (orderCheckVo.getOrderItemVoList() != null && orderCheckVo.getOrderItemVoList().size() != 0 &&
                    orderCheckVo.getHasStockMap() != null && orderCheckVo.getHasStockMap().size() == orderCheckVo.getOrderItemVoList().size()) {
                return R.ok("订单检查页构建完成").put("orderCheckVo", JSON.toJSONString(orderCheckVo));
            } else {
                throw new RRException(BizCodeEnum.BACK_TO_CART.getMessage(), BizCodeEnum.BACK_TO_CART.getCode());
            }
        } else {
            throw new RRException(BizCodeEnum.BLOCKED_BY_SENTINEL.getMessage(), BizCodeEnum.BLOCKED_BY_SENTINEL.getCode());
        }
    }
}
